package com.oauth2.provider.config;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.ApprovalStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;

import com.oauth2.provider.oauth.SparklrUserApprovalHandler;

@Configuration
public class Oauth2ServerConfig {

	private static final String SPARKLR_RESOURCE_ID = "sparklr";

	@Bean
	public TokenStore tokenStore() {
		return new InMemoryTokenStore();
	}

	@Bean
	public ApprovalStore approvalStore(TokenStore tokenStore) {
		TokenApprovalStore store = new TokenApprovalStore();
		store.setTokenStore(tokenStore);
		return store;
	}

	@Bean
	public ApprovalStoreUserApprovalHandler approvalStoreUserApprovalHandler(ApprovalStore approvalStore,
			ClientDetailsService clientDetailsService) {
		SparklrUserApprovalHandler handler = new SparklrUserApprovalHandler();

		handler.setApprovalStore(approvalStore);
		handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
		handler.setClientDetailsService(clientDetailsService);
		handler.setUseApprovalStore(true);

		return handler;
	}

	@Configuration
	@EnableAuthorizationServer
	public static class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

		@Autowired
		private AuthenticationManager authenticationManager;
		
		@Autowired
		private TokenStore tokenStore;
		
		@Autowired
		private ApprovalStoreUserApprovalHandler userApprovalHandler;

		@Value("${tonr.redirect:http://localhost:8080/oauth2_client/callback}")
		private String tonrRedirectUri;

		@Override
		public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
			/*
			 * oauthServer.
			 * tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')"
			 * ) .checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
			 */
			oauthServer.tokenKeyAccess("permitAll()")
					.checkTokenAccess("isAuthenticated()") 
					.allowFormAuthenticationForClients();
		}

		@Override
		public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
			// @formatter:off
			clients.inMemory().withClient("demoApp")
			        .secret("demoAppSecret")
					.authorizedGrantTypes("client_credentials", "password", "refresh_token")
					.authorities("ROLE_CLIENT")
					.scopes("all")
					.resourceIds(SPARKLR_RESOURCE_ID)
					.accessTokenValiditySeconds(1800)
					.refreshTokenValiditySeconds(50000)
					.and().withClient("tonr-with-redirect")
					.resourceIds(SPARKLR_RESOURCE_ID)
					.authorizedGrantTypes("authorization_code", "implicit")
					.authorities("ROLE_CLIENT")
					.scopes("read", "write")
					.secret("secret")
					.redirectUris(tonrRedirectUri);
			// @formatter:on
		}

		@Override
		public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
			endpoints.tokenStore(tokenStore)
			         .userApprovalHandler(userApprovalHandler)
					.authenticationManager(authenticationManager);
		}

	}

	@Configuration
	@EnableResourceServer
	public static class ResourceServerConfig extends ResourceServerConfigurerAdapter {

		public ResourceServerConfig() {
			System.out.println("ResourceServerConfig....");
		}

		@Override
		public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
			resources.resourceId(SPARKLR_RESOURCE_ID).stateless(false);
		}

		@Override
		public void configure(HttpSecurity http) throws Exception {

			http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
				@Override
				public void handle(HttpServletRequest request, HttpServletResponse response,
						AccessDeniedException accessDeniedException) throws IOException, ServletException {
					accessDeniedException.printStackTrace();
				}
			}).authenticationEntryPoint(new AuthenticationEntryPoint() {
				@Override
				public void commence(HttpServletRequest request, HttpServletResponse response,
						AuthenticationException authException) throws IOException, ServletException {
					authException.printStackTrace();
				}
			});

			http
					// .sessionManagement()
					// .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
					// .and()
					.requestMatchers().antMatchers("/rest/**").and().authorizeRequests().antMatchers("/rest/**")
					.authenticated();

		}
	}

}
